/************************************************************************
                                                                     
   Xceed Ultimate ListBox for Silverlight                                                                                                                                            
   Copyright (C) 2010 Xceed Software Inc.    
                                                                     
   This program is provided to you under the terms of the GNU General Public  
   License version 2 as published by the Free Software Foundation. 
        
   This program is distributed in the hope that it will be useful, but
   WITHOUT ANY WARRANTY, without even the implied warranty of 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License along 
   with this program, if not, write to the Free Software Foundation, Inc., 
   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

   This program can be provided to you by Xceed Software Inc. under a
   proprietary commercial license agreement for use in non-Open Source
   projects. Visit Xceed.com to find the commercial edition of 
   Xceed Ultimate ListBox for Silverlight.                                    
                                                                      
 **********************************************************************/

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Xceed.Silverlight.Data;
using Xceed.Silverlight.Utils;
using Xceed.Silverlight.DragDrop;

namespace Xceed.Silverlight.ListBox
{
  [TemplateVisualState( GroupName = "CommonStates", Name = "Disabled" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "Normal" )]
  [TemplateVisualState( GroupName = "CommonStates", Name = "MouseOver" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "Unselected" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "Selected" )]
  [TemplateVisualState( GroupName = "SelectionStates", Name = "SelectedUnfocused" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Unfocused" )]
  [TemplateVisualState( GroupName = "FocusStates", Name = "Focused" )]
  [TemplateVisualState( GroupName = "LoadingStates", Name = "Loaded" )]
  [TemplateVisualState( GroupName = "LoadingStates", Name = "Loading" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Vertical" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Horizontal" )]
  [TemplateVisualState( GroupName = "LayoutModeStates", Name = "Floating" )]
  public class ListBoxContainer : Control, IProvideDraggedContentPresenter
  {
    #region Constructors

    protected ListBoxContainer()
    {
      this.DefaultStyleKey = typeof( ListBoxContainer );
      this.Translation = new TranslateTransform();
      this.Rotation = new RotateTransform();
      this.Scale = new ScaleTransform();
      this.TransformationGroup.Children.Add( this.Translation );
      this.TransformationGroup.Children.Add( this.Scale );
      this.TransformationGroup.Children.Add( this.Rotation );
      this.RenderTransform = this.TransformationGroup;
      this.IsDirty = true;

      this.Loaded += this.OnLoaded;
      this.Unloaded += this.OnUnloaded;
      this.IsEnabledChanged += this.OnIsEnabledChanged;
    }

    #endregion

    #region Content Property

    public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
      "Content",
      typeof( object ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    public object Content
    {
      get
      {
        return ( object )this.GetValue( ListBoxContainer.ContentProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ContentProperty, value );
      }
    }

    #endregion

    #region ContentTemplate Property

    public static readonly DependencyProperty ContentTemplateProperty = DependencyProperty.Register(
      "ContentTemplate",
      typeof( DataTemplate ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    public DataTemplate ContentTemplate
    {
      get
      {
        return ( DataTemplate )this.GetValue( ListBoxContainer.ContentTemplateProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ContentTemplateProperty, value );
      }
    }

    #endregion

    #region DataPath Internal Property

    // Use for Selection
    internal DataPath DataPath
    {
      get
      {
        return m_dataPath;
      }
      set
      {
        if( m_dataPath != value )
        {
          m_dataPath = value;
          if( m_dataPath != null )
          {
            this.IsDataPathLoaded = m_dataPath.LastChild.IsLoaded;
          }
          else
          {
            this.IsDataPathLoaded = false;
          }
        }
      }
    }

    private DataPath m_dataPath; 
    #endregion

    #region IsFocused Internal Property

    internal bool IsFocused
    {
      get
      {
        return m_isFocused;
      }
      set
      {
        m_isFocused = value;
      }
    }

    private bool m_isFocused;

    #endregion

    #region IsVisuallyFocused Internal Property

    internal bool IsVisuallyFocused
    {
      get
      {
        return m_isVisuallyFocused;
      }
      set
      {
        if( m_isVisuallyFocused != value )
        {
          m_isVisuallyFocused = value;
          this.UpdateFocusStates( true );
        }
      }
    }

    private bool m_isVisuallyFocused;

    #endregion

    #region IsDataPathLoaded
    internal bool IsDataPathLoaded
    {
      get
      {
        return m_isDataPathLoaded;
      }
      set
      {
        if( m_isDataPathLoaded != value )
        {
          m_isDataPathLoaded = value;
          this.UpdateLoadingStates( true );
        }
      }
    }

    private bool m_isDataPathLoaded;
    #endregion

    #region IsMouseOver Internal Property

    internal bool IsMouseOver
    {
      get
      {
        return m_isMouseOver;
      }
      set
      {
        if( m_isMouseOver != value )
        {
          m_isMouseOver = value;
          this.UpdateCommonStates( true );
        }
      }
    }

    private bool m_isMouseOver;

    #endregion

    #region IsSelectable Internal Property

    internal virtual bool IsSelectable
    {
      get
      {
        return true;
      }
    }

    #endregion

    #region IsSelected Property

    public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
      "IsSelected",
      typeof( bool ),
      typeof( ListBoxContainer ),
      new PropertyMetadata(
        ( bool )false,
        new PropertyChangedCallback( ListBoxContainer.OnIsSelectedChanged ) ) );

    public bool IsSelected
    {
      get
      {
        return ( bool )this.GetValue( ListBoxContainer.IsSelectedProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.IsSelectedProperty, value );
      }
    }

    private static void OnIsSelectedChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer container = sender as ListBoxContainer;

      if( container == null )
        return;

      container.OnIsSelectedChanged( ( bool )e.OldValue, ( bool )e.NewValue );
    }

    #endregion

    #region ParentListBox Property

    public ListBox ParentListBox
    {
      get
      {
        return ListBox.GetParentListBox( this );
      }
    }

    #endregion


    #region SelectionRange Property

    internal SelectionRange SelectionRange
    {
      get;
      set;
    }

    #endregion

    #region Rotation Property

    internal static readonly DependencyProperty RotationProperty = DependencyProperty.Register(
      "Rotation",
      typeof( RotateTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( ( RotateTransform )null ) );

    internal RotateTransform Rotation
    {
      get
      {
        return ( RotateTransform )this.GetValue( ListBoxContainer.RotationProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.RotationProperty, value );
      }
    }

    #endregion

    #region Translation Property

    internal static readonly DependencyProperty TranslationProperty = DependencyProperty.Register(
      "Translation",
      typeof( TranslateTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    internal TranslateTransform Translation
    {
      get
      {
        return ( TranslateTransform )this.GetValue( ListBoxContainer.TranslationProperty );
      }
      private set
      {
        this.SetValue( ListBoxContainer.TranslationProperty, value );
      }
    }

    #endregion

    #region Scale Property

    internal static readonly DependencyProperty ScaleProperty = DependencyProperty.Register(
      "Scale",
      typeof( ScaleTransform ),
      typeof( ListBoxContainer ),
      new PropertyMetadata( null ) );

    internal ScaleTransform Scale
    {
      get
      {
        return ( ScaleTransform )this.GetValue( ListBoxContainer.ScaleProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ScaleProperty, value );
      }
    }

    #endregion

    #region ZOrder Property

    internal static readonly DependencyProperty ZOrderProperty = DependencyProperty.Register(
      "ZOrder",
      typeof( int ),
      typeof( ListBoxContainer ),
      new PropertyMetadata(
        ( int )0,
        new PropertyChangedCallback( ListBoxContainer.OnZOrderChanged ) ) );

    internal int ZOrder
    {
      get
      {
        return ( int )this.GetValue( ListBoxContainer.ZOrderProperty );
      }
      set
      {
        this.SetValue( ListBoxContainer.ZOrderProperty, value );
      }
    }

    private static void OnZOrderChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer container = sender as ListBoxContainer;

      if( container != null )
        container.SetValue( Canvas.ZIndexProperty, e.NewValue );
    }

    #endregion

    #region Container Configuration Property
    internal ListBoxContainerConfigurationBase Configuration
    {
      get;
      set;
    }
    #endregion

    #region AbsoluteOpacity Property
    internal double AbsoluteOpacity
    {
      get;
      set;
    }
    #endregion

    #region CurrentOffset Property
    internal Point CurrentOffset
    {
      get
      {
        return m_currentPosition;
      }
    }
    #endregion

    #region AbsoluteScaleFactor Property
    internal double AbsoluteScaleFactor
    {
      get
      {
        return m_absoluteScaleFactor;
      }
      set
      {
        if( value != m_absoluteScaleFactor )
        {
          m_absoluteScaleFactor = value;
        }
      }
    }
    #endregion

    #region AbsoluteRotationAngle Property

    internal double AbsoluteRotationAngle
    {
      get
      {
        return m_absoluteRotationAngle;
      }
      set
      {
        double oldValue = m_absoluteRotationAngle;
        if( value != oldValue )
        {
          m_absoluteRotationAngle = value;
        }
      }
    }

    #endregion

    #region AbsoluteOffset Property

    internal Point AbsoluteOffset
    {
      get
      {
        return m_absoluteOffset;
      }
      set
      {
        Point oldValue = m_absoluteOffset;
        if( value != oldValue )
        {
          m_absoluteOffset = value;
        }
      }
    }

    #endregion

    #region AbsoluteElementHeight Property

    internal double AbsoluteElementHeight
    {
      get
      {
        return this.ComputeElementHeight( false );
      }
    }

    #endregion

    #region ActualElementHeight Property

    internal double ActualElementHeight
    {
      get
      {
        return this.ComputeElementHeight( true );
      }
    }

    #endregion

    #region AbsoluteElementWidth Property

    internal double AbsoluteElementWidth
    {
      get
      {
        return this.ComputeElementWidth( false );
      }
    }

    #endregion

    #region ActualElementWidth Property

    internal double ActualElementWidth
    {
      get
      {
        return this.ComputeElementWidth( true );
      }
    }

    #endregion

    #region AbsoluteMaximumHeight Property

    internal double AbsoluteMaximumHeight
    {
      get
      {
        return m_maximumHeight;
      }
    }

    #endregion

    #region AbsoluteMaximumWidth Property

    internal double AbsoluteMaximumWidth
    {
      get
      {
        return m_maximumWidth;
      }
    }

    #endregion

    #region DesiredElementHeight Property

    internal double DesiredElementHeight
    {
      get
      {
        if( this.Configuration == null )
          return 0;

        return this.Configuration.CachedSize.Height;
      }
    }

    #endregion

    #region DesiredElementHeight Property

    internal double DesiredElementWidth
    {
      get
      {
        if( this.Configuration == null )
          return 0;

        return this.Configuration.CachedSize.Width;
      }
    }

    #endregion

    #region MaximumHeight Property
    internal double MaximumHeight
    {
      get
      {
        double retval = m_maximumHeight;

        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          retval += sizeAnimation.ActualMaximumHeightChange;
        }

        m_currentMaximumHeight = retval;

        return retval;
      }
      set
      {
        if( value != m_maximumHeight )
        {
          m_maximumHeight = value;

          if( m_deferSizeUpdateCount == 0 )
          {
            this.UpdateSize();
          }
          //as opposed to the Header and Footer Size, this size do not affect the Layout Manager (it is in fact fed by this one).
        }
      }
    }

    #endregion

    #region Maximum Width Property
    internal double MaximumWidth
    {
      get
      {
        double retval = m_maximumWidth;

        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          retval += sizeAnimation.ActualMaximumWidthChange;
        }

        this.m_currentMaximumWidth = retval;

        return retval;
      }
      set
      {
        if( value != m_maximumWidth )
        {
          m_maximumWidth = value;

          if( m_deferSizeUpdateCount == 0 )
          {
            this.UpdateSize();
          }
          //as opposed to the Header and Footer Size, this size do not affect the Layout Manager (it is in fact fed by this one).
        }
      }
    }
    #endregion

    #region InternalArrangeSize Property

    private Size InternalArrangeSize
    {
      get
      {
        return new Size( this.ActualElementWidth, this.ActualElementHeight );
      }
    }

    #endregion

    #region ActualComputedPosition Property

    internal Point ActualComputedPosition
    {
      get
      {
        return new Point( this.Translation.X, this.Translation.Y );
      }
    }

    #endregion

    #region TransformationGroup Property
    internal TransformGroup TransformationGroup
    {
      get
      {
        return m_transformationGroup;
      }
    }
    #endregion

    #region IsDirty Property
    internal bool IsDirty
    {
      get;
      set;
    }
    #endregion

    #region LayoutMode Attached Property

    private static void OnLayoutModeChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
    {
      ListBoxContainer listBoxContainer = sender as ListBoxContainer;

      if( listBoxContainer != null )
      {
        Debug.Assert( ( ContainerLayoutMode )e.NewValue != ContainerLayoutMode.Default );
        listBoxContainer.UpdateLayoutModeStates( true );
      }
    }

    public static ContainerLayoutMode GetLayoutMode( DependencyObject obj )
    {
      return ( ContainerLayoutMode )obj.GetValue( ListBoxContainer.LayoutModeProperty );
    }

    public static void SetLayoutMode( DependencyObject obj, ContainerLayoutMode value )
    {
      obj.SetValue( ListBoxContainer.LayoutModeProperty, value );
    }

    public static readonly DependencyProperty LayoutModeProperty =
        DependencyProperty.RegisterAttached( "LayoutMode", typeof( ContainerLayoutMode ), typeof( ListBoxContainer ), 
        new PropertyMetadata( ContainerLayoutMode.Default, new PropertyChangedCallback( ListBoxContainer.OnLayoutModeChanged ) ) );

    #endregion

    #region Internal PositioningLayoutMode Property

    internal ContainerLayoutMode PositioningLayoutMode
    {
      get
      {
        return m_positioningLayoutMode;
      }
      set
      {
        if( m_positioningLayoutMode != value )
        {
          if( value == ContainerLayoutMode.Default )
            throw new InvalidOperationException( "PositioningLayoutMode should never be set to ContainerLayoutMode.Default." );

          m_positioningLayoutMode = value;
        }
      }
    }

    #endregion


    #region Public Methods

    public override void OnApplyTemplate()
    {
      base.OnApplyTemplate();

      ListBox parentListBox = this.ParentListBox;

      // Force the ListBox to clear its configuration
      // and reset the layout as soon as possible
      if( parentListBox != null )
        parentListBox.DeferClearContainerConfigurationCache();

      this.ChangeVisualState( false );
    }

    protected override void OnKeyDown( KeyEventArgs e )
    {
      base.OnKeyDown( e );

      if( this.ParentListBox != null )
        this.ParentListBox.Panel.ProcessListBoxContainerKeyDown( this, e );
    }

    #endregion Public Methods

    #region Protected Methods

    protected virtual DragDropContext CreateDragDropContextCore()
    {
      ListBox parentListBox = this.ParentListBox;

      View activeView = MultiViewPanel.GetActiveView( parentListBox );

      DraggedElementConfiguration draggedElementConfiguration = ( activeView != null )
        ? activeView.DraggedElementConfiguration
        : parentListBox.ContainerConfigurations.DraggedElementConfiguration;

      Debug.Assert( draggedElementConfiguration != null, "We should always have an instance of a DraggedElementConfiguration to pass to the DragContext." );

      IEnumerable<FrameworkElement> siblingDraggedElements = null;

      if( draggedElementConfiguration != null )
      {
        siblingDraggedElements = this.GetSiblingDraggedElements();
      }

      return new DragDropContext( this, draggedElementConfiguration, siblingDraggedElements );
    }

    protected virtual IEnumerable<FrameworkElement> GetSiblingDraggedElementsCore()
    {
      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return null;

      PanelBase panelBase = parentListBox.Panel;

      if( panelBase == null )
        return null;

      List<FrameworkElement> siblingElements = null;

      if( panelBase != null )
      {
        // Get a list of all ListBoxContainer that are currently in the Visual Tree
        // and have IsSelected = true
        UIElementCollection children = panelBase.Children;

        foreach( ListBoxContainer listBoxContainer in children )
        {
          if( listBoxContainer == this )
            continue;

          if( ( listBoxContainer.IsSelected ) && ( this.GetCalculatedCanDrag() ) )
          {
            if( siblingElements == null )
            {
              siblingElements = new List<FrameworkElement>();
            }

            siblingElements.Add( listBoxContainer );
          }
        }
      }

      return siblingElements;
    }

    internal void OnIsEnabledChanged( object sender, DependencyPropertyChangedEventArgs e )
    {
      this.UpdateCommonStates( true );
    }

    internal void OnIsSelectedChanged( bool oldValue, bool newValue )
    {
      if( !this.IsSelectable && newValue )
        throw new InvalidOperationException( "Can't select a non-selectable container." );

      this.UpdateSelectionStates( false );

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox != null )
        parentListBox.OnListBoxContainerIsSelectedChanged( this );
    }

    protected virtual void OnLoaded()
    {
    }

    protected virtual void OnUnloaded()
    {
    }

    protected override void OnGotFocus( RoutedEventArgs e )
    {
      base.OnGotFocus( e );
      bool hasFocus = FocusHelper.HasFocus( this );
      this.FocusChanged( hasFocus );
    }

    protected override void OnLostFocus( RoutedEventArgs e )
    {
      base.OnLostFocus( e );
      bool hasFocus = FocusHelper.HasFocus( this );
      this.FocusChanged( hasFocus );
    }

#if !WINDOWS_PHONE
    protected override void OnMouseEnter( MouseEventArgs e )
    {
      base.OnMouseEnter( e );
      this.IsMouseOver = true;
    }

    protected override void OnMouseLeave( MouseEventArgs e )
    {
      base.OnMouseLeave( e );
      this.IsMouseOver = false;
    }

    protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonDown( e );

      if( e.Handled )
        return;

      ListBox parentListBox = this.ParentListBox;
      Debug.Assert( this.ParentListBox != null );

      if( parentListBox == null )
        return;

      e.Handled = true;

      if( this.Focus() )
      {
        DataPath dataPath = this.DataPath;

        // Do not affect selection when the DataPath is not loaded. A 
        // Prepare container will be called when the replace is processed and
        // the focused container will be the one matching the focused DataPath
        if( ( dataPath != null ) && ( dataPath.LastChild != null ) && ( dataPath.LastChild.IsLoaded ) )
          parentListBox.UpdateSelection( this, SelectionAction.MouseDown );
      }

      parentListBox.Panel.ProcessListBoxContainerMouseDown( this, e, !this.GetHasRunningTransitionAnimation() );

      if( this.GetCalculatedCanDrag() )
      {
        DragDropContext dragDropContext = this.CreateDragDropContext();

        dragDropContext.DragStarting += new EventHandler<DragDropCancelEventArgs>( this.OnDragDropContextDragStarting );

        DragDropManager.PrepareDrag( dragDropContext, e );
      }
    }

    private void OnDragDropContextDragStarting( object sender, DragDropCancelEventArgs e )
    {
      ListBox parentListBox = this.ParentListBox;

      if( parentListBox != null )
      {
        // Forward the event to the ListBox
        parentListBox.OnDragStarting( e );
      }
      else
      {
        // Can't see how this could happen, if it does cancel.
        Debug.Assert( false, "How could this happen?" );
        e.Cancel = true;
      }
    }

    protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
    {
      base.OnMouseLeftButtonUp( e );

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return;

      DragDropContext currentDragDropContext = DragDropManager.CurrentDragDropContext;

      if( ( currentDragDropContext == null ) ||
        ( ( currentDragDropContext != null ) && ( currentDragDropContext.DraggedElement != this ) ) )
      {
        parentListBox.UpdateSelection( this, SelectionAction.MouseUp );
      }
    }

    protected override void OnMouseWheel( MouseWheelEventArgs e )
    {
      ListBox listBox = this.ParentListBox;

      if( listBox != null )
      {
        PanelBase panel = listBox.Panel;

        if( panel != null )
        {
          panel.ProcessMouseWheel( e );
          e.Handled = true;
        }
      }

      // Call base after the ParentListBox to be able to handle
      // the wheel action before the IScrollInfo implementation of the 
      // PanelBase does.
      base.OnMouseWheel( e );
    }
#else
    protected override void OnManipulationStarted( ManipulationStartedEventArgs e )
    {
      base.OnManipulationStarted( e );

      if( e.Handled == true )
        return;

      ListBox parentListBox = this.ParentListBox;
      Debug.Assert( this.ParentListBox != null );

      if( parentListBox == null )
        return;

      e.Handled = true;

      parentListBox.Panel.InitializeManipulation();
    }

    protected override void OnManipulationDelta( ManipulationDeltaEventArgs e )
    {
      base.OnManipulationDelta( e );

      if( e.Handled == true )
        return;

      this.ParentListBox.Panel.ApplyManipulationDelta( e.DeltaManipulation.Translation, e.CumulativeManipulation.Translation, this.ActualHeight, this.ActualWidth );
    }

    protected override void OnManipulationCompleted( ManipulationCompletedEventArgs e )
    {
      base.OnManipulationCompleted( e );

      if( e.Handled == true )
        return;

      ListBox parentListBox = this.ParentListBox;

      if( parentListBox == null )
        return;

      e.Handled = true;

      if( GetIsManipulationEmpty( e ) )
      {
        // This is a tap manipulation, so do both MouseDown and MouseUp
        parentListBox.Panel.ProcessListBoxContainerMouseDown( this, null, !this.GetHasRunningTransitionAnimation() );
        parentListBox.UpdateSelection( this, SelectionAction.MouseDown );
        parentListBox.UpdateSelection( this, SelectionAction.MouseUp );
      }

      this.ParentListBox.Panel.CompleteManipulation( e.IsInertial, e.TotalManipulation, e.FinalVelocities, this.ActualHeight, this.ActualWidth );
    }
#endif

    protected override Size ArrangeOverride( Size finalSize )
    {
      if( finalSize != m_originalArrangeFinalSize )
      {
        m_arrangeReturnSize = base.ArrangeOverride( finalSize );

        m_originalArrangeFinalSize = finalSize;
      }

      Size newArrangeSize = this.InternalArrangeSize;

      base.ArrangeOverride( newArrangeSize );

      m_lastRealArrangeSize = newArrangeSize;

      return m_arrangeReturnSize;
    }

    protected virtual ContentPresenter CreateDraggedContentPresenter()
    {
      return new ContentPresenter();
    }

    protected virtual void PrepareDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      ListBox.SetParentListBox( contentPresenter, this.ParentListBox );
    }

    protected virtual void ClearDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      contentPresenter.ClearValue( ListBox.ParentListBoxProperty );
    }
    #endregion Protected Methods

    #region Internal Methods

    internal void ChangeVisualState()
    {
      this.ChangeVisualState( true );
    }

    protected internal virtual void ChangeVisualState( bool useTransitions )
    {
      this.UpdateLayoutModeStates( useTransitions );

      this.UpdateCommonStates( useTransitions );

      this.UpdateSelectionStates( useTransitions );

      this.UpdateFocusStates( useTransitions );

      this.UpdateLoadingStates( useTransitions );
    }

    internal bool GoToState( bool useTransitions, string stateName )
    {
      return VisualStateManager.GoToState( this, stateName, useTransitions );
    }

    internal virtual void SetDataContextFromDataPath( DataPath dataPath )
    {
      this.DataContext = dataPath;
      this.Content = dataPath;
    }

    internal void InvalidatePrepare()
    {
      this.IsDirty = true;
    }

    #endregion Internal Methods

    #region Internal Animations Related Methods

    internal void BeginSizeUpdateDeferring()
    {
      m_deferSizeUpdateCount++;
    }

    internal void EndSizeUpdateDeferring()
    {
      if( m_deferSizeUpdateCount == 0 )
        return;

      m_deferSizeUpdateCount--;

      if( m_deferSizeUpdateCount == 0 )
      {
        this.UpdateSize();
      }
    }

    private double ComputeElementHeight( bool includeTransitions )
    {
      double computedValue = 0.0d;
      double margins = this.Margin.Top + this.Margin.Bottom;
      double desiredElementHeightWithMargins = this.DesiredElementHeight - margins;

      if( this.PositioningLayoutMode != ContainerLayoutMode.Horizontal )
      {
        computedValue = desiredElementHeightWithMargins;
      }
      else
      {
        if( !includeTransitions )
          return m_maximumHeight - margins;

        computedValue = m_maximumHeight - margins;
      }

      double maximumHeight = m_maximumHeight;

      if( includeTransitions )
      {
        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          computedValue += sizeAnimation.ActualHeightChange;
        }

        maximumHeight = this.MaximumHeight;
      }

      computedValue = Math.Max( computedValue, desiredElementHeightWithMargins );

      if( computedValue > maximumHeight - margins )
      {
        computedValue = maximumHeight - margins;
      }

      if( computedValue < 0d )
      {
        computedValue = 0d;
      }

      return computedValue;
    }

    private double ComputeElementWidth( bool includeTransitions )
    {
      double computedValue = 0;
      double margins = this.Margin.Left + this.Margin.Right;
      double desiredElementWidthWithMargins = this.DesiredElementWidth - margins;

      if( this.PositioningLayoutMode != ContainerLayoutMode.Vertical )
      {
        computedValue = desiredElementWidthWithMargins;
      }
      else
      {
        if( !includeTransitions )
          return m_maximumWidth - margins;

        computedValue = m_maximumWidth - margins;
      }

      double maximumWidth = m_maximumWidth;

      if( includeTransitions )
      {
        SizeAnimation sizeAnimation = this.GetAnimation( typeof( SizeAnimation ) ) as SizeAnimation;

        if( sizeAnimation != null )
        {
          computedValue += sizeAnimation.ActualWidthChange;
        }

        maximumWidth = this.MaximumWidth;
      }

      computedValue = Math.Max( computedValue, desiredElementWidthWithMargins );

      if( computedValue > maximumWidth - margins )
      {
        computedValue = maximumWidth - margins;
      }

      if( computedValue < 0d )
      {
        computedValue = 0d;
      }

      return computedValue;
    }

    internal void AddAnimation( IAnimation newAnimation )
    {
      IAnimation currentAnimation = this.GetAnimation( newAnimation.GetType() );

      // Replace the current animation of a type by the 
      // new animation of that same type
      if( currentAnimation != null )
      {
        m_animationList.Remove( currentAnimation );
      }

      m_animationList.Add( newAnimation );
    }


    internal void RemoveAllAnimations()
    {
      m_animationList.Clear();
    }


    internal void RemoveAnimation( Type animationType, TransitionAnimation transitionAnimation )
    {

      IAnimation currentAnimation = GetAnimation( animationType );

      if( ( currentAnimation != null ) && ( currentAnimation.ParentTransition == transitionAnimation ) )
        m_animationList.Remove( currentAnimation );

    }

    internal IAnimation GetAnimation( Type animationType )
    {
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.GetType() == animationType )
          return animation;
      }

      return null;
    }

    internal bool IsAnimatedBy( Type animationType )
    {
      return this.GetAnimation( animationType ) != null;
    }

    internal List<Type> GetActiveAnimationsType( TransitionAnimation transition )
    {
      List<Type> animationTypeList = new List<Type>();
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.ParentTransition == transition )
        {
          animationTypeList.Add( animation.GetType() );
        }
      }

      return animationTypeList;
    }

    internal void UpdateAnimations( double elapsedTime )
    {
      foreach( IAnimation animation in m_animationList )
      {
        animation.ComputeAnimationUpdate( elapsedTime );
      }
    }

    internal void UpdateLayoutTransform()
    {
      double positionX = this.AbsoluteOffset.X;
      double positionY = this.AbsoluteOffset.Y;
      double rotation = this.AbsoluteRotationAngle;
      double scaleFactor = this.AbsoluteScaleFactor;
      double width = this.ActualElementWidth;
      double height = this.ActualElementHeight;
      double opacity = this.AbsoluteOpacity;

      PositionAnimation positionAnimation = this.GetAnimation( typeof( PositionAnimation ) ) as PositionAnimation;

      if( positionAnimation != null )
      {
        positionX += positionAnimation.ActualXChange;
        positionY += positionAnimation.ActualYChange;
      }

      RotateAnimation rotateAnimation = this.GetAnimation( typeof( RotateAnimation ) ) as RotateAnimation;
      if( rotateAnimation != null )
      {
        if( !Double.IsNaN( rotateAnimation.ActualAngleChange ) )
          rotation += rotateAnimation.ActualAngleChange;
      }

      ScaleAnimation scaleAnimation = this.GetAnimation( typeof( ScaleAnimation ) ) as ScaleAnimation;
      if( scaleAnimation != null )
      {
        if( !Double.IsNaN( scaleAnimation.ActualScaleChange ) )
          scaleFactor += scaleAnimation.ActualScaleChange;
      }
      m_currentPosition.X = positionX;
      m_currentPosition.Y = positionY;

      this.Translation.X = positionX;
      this.Translation.Y = positionY;
      this.Scale.CenterX = positionX;
      this.Scale.CenterY = positionY;
      this.Scale.ScaleX = scaleFactor;
      this.Scale.ScaleY = scaleFactor;


      if( !Double.IsInfinity( width ) )
        this.Rotation.CenterX = positionX + ( width / 2.0d );

      if( !Double.IsInfinity( height ) )
        this.Rotation.CenterY = positionY + ( height / 2.0d );

      this.Rotation.Angle = rotation;

      OpacityAnimation opacityAnimation = this.GetAnimation( typeof( OpacityAnimation ) ) as OpacityAnimation;

      if( opacityAnimation != null )
      {
        if( !Double.IsNaN( opacityAnimation.ActualOpacityChange ) )
          opacity += opacityAnimation.ActualOpacityChange;
      }

      this.Opacity = opacity;
    }

    internal void UpdateSize()
    {

      Size newSize = new Size( this.ActualElementWidth, this.ActualElementHeight );

      if( m_lastRealArrangeSize != newSize )
      {
        this.InvalidateArrange();

        //TODO: the following line is to avoid the flickering caused by the re-arrangements of controls. Forces a layout pass for the concerned element
        this.UpdateLayout();
      }


    }


    #endregion

    #region Recycling

    internal void OnRecycledAndLayoutedOutOfView()
    {
      // Reset RecycleEvenIfFocused.
      m_recycleEvenIfFocused = null;
    }

    internal bool GetIsElligibleForRecycling()
    {
      if( this.GetHasRunningTransitionAnimation() )
        return false;

      if( this.RecycleEvenIfFocused )
      {
        return true;
      }

      return !FocusHelper.HasFocus( this );
    }

    internal bool RecycleEvenIfFocused
    {
      get
      {
        if( !m_recycleEvenIfFocused.HasValue )
        {
          return this.DefaultRecycleEvenIfFocused;
        }

        return m_recycleEvenIfFocused.Value;
      }
      set
      {
        m_recycleEvenIfFocused = value;
      }
    }

    internal virtual bool DefaultRecycleEvenIfFocused
    {
      get
      {
        return false;
      }
    }

    private bool? m_recycleEvenIfFocused;

    #endregion


    #region Private Methods

    private DragDropContext CreateDragDropContext()
    {
      return this.CreateDragDropContextCore();
    }

    private IEnumerable<FrameworkElement> GetSiblingDraggedElements()
    {
      return this.GetSiblingDraggedElementsCore();
    }

    private bool GetCalculatedCanDrag()
    {
      ListBoxContainerConfigurationBase configuration = this.Configuration;

      Debug.Assert( configuration != null );

      if( ( configuration != null ) && ( configuration.CanDrag ) && ( this.ParentListBox.AllowDrag )
        && ( this.IsDataPathLoaded ) && ( !this.GetHasRunningTransitionAnimation<RemovingItemTransitionAnimation>() ) )
      {
        return true;
      }

      return false;
    }

    private void OnLoaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
      this.OnLoaded();
    }

    private void OnUnloaded( object sender, RoutedEventArgs e )
    {
      this.ChangeVisualState( false );
      this.OnUnloaded();
    }

    private void FocusChanged( bool hasFocus )
    {
      this.IsFocused = hasFocus;
      this.IsVisuallyFocused = hasFocus;
    }

    private bool IsParentSelectionActive()
    {
      //TODO mimic ListBox verifying if at least 1 item is selected
      return ( this.ParentListBox != null ) && ( ( this.ParentListBox.Panel.LayoutManager.GetContainerFromPath( this.ParentListBox.CurrentDataPath ) != null ) );
    }

    private void UpdateCommonStates( bool useTransitions )
    {
      if( !base.IsEnabled )
      {
        this.GoToState( useTransitions, "Disabled" );
      }
      else if( this.IsMouseOver )
      {
        this.GoToState( useTransitions, "MouseOver" );
      }
      else
      {
        this.GoToState( useTransitions, "Normal" );
      }
    }

    private void UpdateSelectionStates( bool useTransitions )
    {
      if( this.IsSelected )
      {
        if( this.IsParentSelectionActive() )
        {
          this.GoToState( useTransitions, "Selected" );
        }
        else
        {
          if( !this.GoToState( useTransitions, "SelectedUnfocused" ) )
          {
            this.GoToState( useTransitions, "Selected" );
          }
        }
      }
      else
      {
        this.GoToState( useTransitions, "Unselected" );
      }
    }

    private void UpdateLoadingStates( bool useTransitions )
    {
      if( this.IsDataPathLoaded )
      {
        this.GoToState( useTransitions, "Loaded" );
      }
      else
      {
        this.GoToState( useTransitions, "Loading" );
      }

    }

    private void UpdateFocusStates( bool useTransitions )
    {
      if( this.IsVisuallyFocused )
      {
        this.GoToState( useTransitions, "Focused" );
      }
      else
      {
        this.GoToState( useTransitions, "Unfocused" );
      }
    }

    private void UpdateLayoutModeStates( bool useTransitions )
    {
      switch( ListBoxContainer.GetLayoutMode( this ) )
      {
        case ContainerLayoutMode.Vertical:
        {
          this.GoToState( useTransitions, "Vertical" );
          break;
        }

        case ContainerLayoutMode.Horizontal:
        {
          this.GoToState( useTransitions, "Horizontal" );
          break;
        }

        case ContainerLayoutMode.Floating:
        default:
        {
          this.GoToState( useTransitions, "Floating" );
          break;
        }

      }
    }

    private bool GetHasRunningTransitionAnimation()
    {
      return m_animationList.Count > 0;
    }

    private bool GetHasRunningTransitionAnimation<T>()
      where T : TransitionAnimation
    {
      foreach( IAnimation animation in m_animationList )
      {
        if( animation.ParentTransition is T )
          return true;
      }

      return false;
    }

#if WINDOWS_PHONE
    private bool GetIsManipulationEmpty( ManipulationCompletedEventArgs e )
    {
      Point emptyPoint = new Point();
      return ( e.TotalManipulation.Scale.Equals( emptyPoint ) ) &&
        ( e.TotalManipulation.Translation.Equals( emptyPoint ) ) &&
        ( ( !e.IsInertial ) ||
        ( e.FinalVelocities.ExpansionVelocity.Equals( emptyPoint ) ) &&
        ( e.FinalVelocities.LinearVelocity.Equals( emptyPoint ) ) );
    }
#endif

    #endregion Private Methods

    #region Private Fields

    private Point m_absoluteOffset;
    private double m_absoluteRotationAngle = 0.0;
    private double m_absoluteScaleFactor = 1.0;
    private int m_deferSizeUpdateCount = 0;

    private Point m_currentPosition = new Point( 0.0d, 0.0d );
    private double m_currentMaximumWidth = 0.0d;
    private double m_currentMaximumHeight = 0.0d;


    private double m_maximumHeight = double.PositiveInfinity;
    private double m_maximumWidth = double.PositiveInfinity;
    private Size m_lastRealArrangeSize = Size.Empty;
    private Size m_originalArrangeFinalSize = Size.Empty;
    private Size m_arrangeReturnSize = Size.Empty;

    // This ContainerLayoutMode is used to calculate the height and width of the container and will
    // match the one used by the panel/active view to position the containers.  
    // This must never return ContainerLayoutMode.Default.
    //
    // On the other hand, the LayoutMode Attached Property and its corresponding local clr property is used to drive the container's 
    // visual state in order to offer a configurable way to change the container's template depending on the 
    // positioning of either the panel/active view (when set to ContainerLayoutMode.Default) or specified through 
    // the container's configuration.
    private ContainerLayoutMode m_positioningLayoutMode = ContainerLayoutMode.Floating;

    private TranslateTransform m_positionTransform = new TranslateTransform();
    private TransformGroup m_transformationGroup = new TransformGroup();

    private List<IAnimation> m_animationList = new List<IAnimation>();

    #endregion


    #region IProvideDraggedContentPresenter Members

    ContentPresenter IProvideDraggedContentPresenter.CreateDraggedContentPresenter()
    {
      ContentPresenter contentPresenter = this.CreateDraggedContentPresenter();

      contentPresenter.RenderTransform = this.RenderTransform;

      ListBox listBox = this.ParentListBox;

      if( listBox == null )
        throw new ListBoxInternalException( "A recycled container was used as drag source." );

      // We must translate the container's top left point to the top left corner of the Panel
      // since the DragDropAdornerManager will force a SetLeft and SetTop to the position
      // of the draggedElement according to the RootElement of the 
      Point itemToPanel = this.TransformToVisual( listBox.Panel ).Transform( new Point() );
      Canvas.SetLeft( contentPresenter, -itemToPanel.X );
      Canvas.SetTop( contentPresenter, -itemToPanel.Y );

      return contentPresenter;
    }

    void IProvideDraggedContentPresenter.PrepareDraggedContentPresenter(ContentPresenter contentPresenter)
    {
      this.PrepareDraggedContentPresenter( contentPresenter );
    }

    void IProvideDraggedContentPresenter.ClearDraggedContentPresenter( ContentPresenter contentPresenter )
    {
      this.ClearDraggedContentPresenter( contentPresenter );
    }
    #endregion
  }
}
